package com.backpackerxl.blog.util;

import org.commonmark.Extension;
import org.commonmark.ext.gfm.tables.TableBlock;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.ext.heading.anchor.HeadingAnchorExtension;
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.*;

import java.util.*;

/**
 * Created by limi on 2017/10/22.
 */
public class MarkdownUtils {
    private MarkdownUtils() {
    }

    /**
     * markdown格式转换成HTML格式
     *
     * @param markdown 格式
     * @return 渲染好的格式
     */
    @Deprecated
    public static String markdownToHtml(String markdown) {
        Parser parser = Parser.builder().build();
        Node document = parser.parse(markdown);
        HtmlRenderer renderer = HtmlRenderer.builder().build();
        return renderer.render(document);
    }

    /**
     * 增加扩展[标题锚点，表格生成]
     * Markdown转换成HTML
     *
     * @param markdown 格式
     * @return 自定义
     */
    public static String markdownToHtmlExtensions(String markdown) {
        //h标题生成id
        Set<Extension> headingAnchorExtensions = Collections.singleton(HeadingAnchorExtension.create());
        //转换table的HTML
        List<Extension> tableExtension = Collections.singletonList(TablesExtension.create());
        Parser parser = Parser.builder()
                .extensions(tableExtension)
                .build();
        Node document = parser.parse(markdown);
        HtmlRenderer renderer = HtmlRenderer.builder()
                .extensions(headingAnchorExtensions)
                .extensions(tableExtension)
                .attributeProviderFactory(context -> new CustomAttributeProvider())
                .nodeRendererFactory(AppendIcon::new)
                .build();
        return renderer.render(document);
    }

    /**
     * 处理标签的属性
     */
    static class CustomAttributeProvider implements AttributeProvider {
        @Override
        public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
            //改变a标签的target属性为_blank
            if (node instanceof Link) {
                attributes.put("target", "_blank");
            }
            if (node instanceof TableBlock) {
                attributes.put("class", "ui celled table");
            }
        }
    }

    public static String html(String markdown) {
        Parser parser = Parser.builder().build();
        Node document = parser.parse(markdown);

        HtmlRenderer renderer = HtmlRenderer.builder().
                attributeProviderFactory(attributeProviderContext -> new PAttributeProvider())
                .nodeRendererFactory(AppendIcon::new).
                build();
        return renderer.render(document);
    }

    private static class PAttributeProvider implements AttributeProvider {
        @Override
        public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
            if (node instanceof Image) {
                attributes.put("style", "width:150px;height:200px;position:relative;left:50%;margin-left:-100px;");
            }
            if (node instanceof Link) {
                attributes.put("target", "_blank");
            }
        }
    }

    private static class AppendIcon extends CoreHtmlNodeRenderer implements NodeRenderer {
        private final HtmlWriter html;

        public AppendIcon(HtmlNodeRendererContext context) {
            super(context);
            this.html = context.getWriter();
        }

        @Override
        public void visit(FencedCodeBlock fencedCodeBlock) {
            String literal = fencedCodeBlock.getLiteral();
            if (literal == null) {
                literal = " ";
            }
            Map<String, String> attributes = new LinkedHashMap<>();
            String info = fencedCodeBlock.getInfo();
            if (info != null && !info.isEmpty()) {
                int space = info.indexOf(" ");
                String language;
                if (space == -1) {
                    language = info;
                } else {
                    language = info.substring(0, space);
                }

                attributes.put("class", "language-" + language);
            }

            this.renderCodeBlock(literal, fencedCodeBlock, attributes);
        }

        private Map<String, String> getAttrs(Node node, String tagName) {
            return this.getAttrs(node, tagName, Collections.emptyMap());
        }

        private Map<String, String> getAttrs(Node node, String tagName, Map<String, String> defaultAttributes) {
            return this.context.extendAttributes(node, tagName, defaultAttributes);
        }

        private void renderCodeBlock(String literal, Node node, Map<String, String> attributes) {
            this.html.line();
            this.html.tag("pre", this.getAttrs(node, "pre"));
            this.html.tag("code", this.getAttrs(node, "code", attributes));
            this.html.text(literal.substring(0, literal.length() - 1));
            this.html.tag("/code");
            this.html.tag("i", this.getAttrs(node, "i", Collections.singletonMap("class", "copy link icon")));
            this.html.tag("/i");
            this.html.tag("/pre");
            this.html.line();
        }
    }
}
